#include "sprite.hpp"
#include "engine.hpp"
#include <SDL_image.h>
#include "errors.hpp"

namespace wolf
{
void Sprite::_init(Uint16 nCols, Uint16 nRows)
{
	this->nColumns = nCols;
	this->frameCount = nCols * nRows;
	this->width = image->w / nCols;
	this->height = image->h / nRows;
	velocity = 1;
	int gFrame = Engine::getInstance().getCurrentFrame();
	setOffset(((gFrame / frameCount) + 1) * frameCount - gFrame);
}

Sprite::Sprite(const char *imageFile, int nCols, int nRows)
{
	image = IMG_Load(imageFile);
	if (!image)
		throw SdlError();

	_init(nCols, nRows);
	//	width = nCols;
	//	height = nRows;
	//	velocity = 1;
	//	nColumns = (image->w / nCols);
	//	frameCount = (image->w / nCols) * (image->h / nRows);
	//	int gframe = Engine::getInstance().getCurrentFrame();
	//	setOffset(((gframe / frameCount) + 1) * frameCount - gframe);
}

Sprite::Sprite(SDL_Surface *image, int nCollumns, int nRows, bool own)
{
	WOLF_ASSERT(image != NULL);
	this->image = image;
	if (!own)
		++image->refcount;
	_init(nCollumns, nRows);
}

Sprite::Sprite(SDL_RWops *src, int nCollumns, int nRows, bool freesrc, const char *type)
{
	if (type)
		image = IMG_LoadTyped_RW(src, freesrc, const_cast<char*>(type));
	else
		image = IMG_Load_RW(src, freesrc);
	if (!image)
		throw SdlError();
	_init(nCollumns, nRows);
}

Sprite::~Sprite()
{
	SDL_FreeSurface(image);
}

Uint16 Sprite::getFrame(void) const
{
	return (Uint16) (((((offset + int(Engine::getInstance().getCurrentFrame() * velocity)))))) % frameCount;
}

void Sprite::setFrame(Uint16 ind)
{
	Uint16 curIndex = getFrameCount();
	if (ind < curIndex)
	{
		Uint16 delta = curIndex - ind;
		if (offset > delta)
			offset -= delta;

		else
			offset = frameCount + offset - delta;

	}
	else if (ind > curIndex)
	{
		Uint16 delta = ind - curIndex;
		if (offset + delta < frameCount)
			offset += delta;

		else
			offset = frameCount - offset + delta;

	}

}

void Sprite::draw(SDL_Surface *dst, int x, int y)
{
	Sint16 index = (offset + int(Engine::getInstance().getCurrentFrame() * velocity)) % frameCount;
	Sint16 line = index / nColumns;
	Sint16 collumn = index % nColumns;
	SDL_Rect slice =
	{ Sint16(collumn * width), Sint16(line * height), width, height };
	SDL_Rect position =
	{ Sint16(x), Sint16(y) };
	SDL_BlitSurface(image, &slice, dst, &position);
}

void Sprite::setOffset(unsigned value)
{
	offset = value % frameCount;
}

unsigned Sprite::getOffset() const
{
	return offset;
}

void Sprite::setVelocity(double vel)
{
	velocity = vel;
}

double Sprite::getVelocity() const
{
	return velocity;
}

SDL_Surface *Sprite::getImage()
{
	return image;
}

Sprite::Sprite(const Sprite & other)
{
	rawCopy(other);
	++image->refcount;
}

Sprite & Sprite::operator =(const Sprite & other)
{
	// check for assignment to self
	if (this == &other)
		return *this;

	SDL_FreeSurface(image);
	rawCopy(other);
	++image->refcount;
	return *this;
}
Uint16 Sprite::getFrameCount()
{
	return frameCount;
}

void Sprite::rawCopy(const Sprite & other)
{
	offset = other.offset;
	velocity = other.velocity;
	image = other.image;
	nColumns = other.nColumns;
	frameCount = other.frameCount;
	width = other.width;
	height = other.height;
}

bool Sprite::transparent(unsigned line, unsigned col) const
{
	SDL_PixelFormat & format = *image->format;
	Uint32 pixel = 0;
	Uint8 Bpp = format.BytesPerPixel;
	Rect myRect = getRect();
	Uint8 *ptr = ((Uint8*) ((((image->pixels))))) + (line + myRect.getY()) * (col + myRect.getX()) * Bpp;
	for (int i = 0; i < Bpp; ++i)
		pixel |= (Uint32) (((((ptr[i]))))) << 8 * i;

	if (format.Amask)
	{
		Uint8 r, g, b, a;
		SDL_GetRGBA(pixel, &format, &r, &g, &b, &a);
		return !(a & 80);
	}
	else
	{
		return pixel == format.colorkey;
	}
}

}
